home *** CD-ROM | disk | FTP | other *** search
- program reformat;
- {
- Program to reformat any disk attached to a Olivetti PC or compatible.
- The program will probably work well on any MS/PC-DOS machine running under
- DOS 2.xx. Fixed disks of all sizes
-
- ** Modified May 16, 1986 By Rick Watson
- **
- ** Original program did not know about long (16 bit) FAT's.
- ** Therefore the program blew up with disks with over 4K
- ** clusters. Made changes necessary to accomodate disks
- ** up to 16K clusters.
- **
- ** Also found that if the program is run on the default disk,
- ** a directory displayed garbage upon completion and DOS could
- ** not find any files. This is because the program rearranged
- ** the FAT's and directories without telling DOS. DOS keeps
- ** information in memory about the disk that no longer matches
- ** reality. This can cause a great deal of tension when the
- ** user comes to the conclusion that his disk has been trashed.
- ** I have changed the program to require the user to reboot
- ** the system upon completion if it is run on the default drive.
- **
- ** I have tested this program with my 20 Meg hard
- ** disk, a 2 Meg RAMdisk and 360K floppies. All testing seems
- ** to be successful, however...
- ** Since this program rewrites the FAT's, directories,
- ** sub-directories, and file data, it constitutes a risk.
- ** An undetected program bug, power interruption during use,
- ** a well directed cosmic ray, etc., etc., etc. could cause
- ** total and irreversible loss of ALL data on the disk being
- ** reformatted (The Norton Utilities will just laugh at you).
- **
- ** USE AT YOUR OWN RISK!
- ** (being backed up helps)
- **
- Global types }
-
- type
-
- Regpack = record case integer of
- 1: (ax, bx, cx, dx, bp, si, di, ds, es, flags : integer);
- 2: (al, ah, bl, bh, cl, ch, dl, dh : byte);
- end;
-
- Boot = record
- Jump: array[0..2] of byte;
- OEM : array[0..7] of char;
- SectorSize: integer;
- Clustersize: byte;
- ReservedSectors: integer;
- NumberOfFats: byte;
- RootDirSize,
- TotalSectors: integer;
- MediaDescriptor: byte;
- FatSize,
- TrackSize,
- NumberOfHeads,
- NumberOfHiddenSectors: integer;
- end;
-
- IntArray = array[0..32766] of integer;
-
- Buffer = array[0..32766] of byte;
-
- LongInteger = array[0..1] of integer;
-
- DirectoryPointer = ^DirectoryEntry;
-
- DirectoryEntry = record
- EntryName: array[0..10] of char;
- Attribute: byte;
- Reserved: array[1..10] of byte;
- TimeLastUpdated: integer;
- DateLastUpdated: integer;
- StartingCluster: integer;
- Filesize: LongInteger;
- NewStartingCluster: integer;
- Next,
- SubDirectory: DirectoryPointer;
- end;
-
- WorkString = string[255];
-
- const
-
- ReadOnly: byte = $01;
- HiddenFile: byte = $02;
- SystemFile: byte = $04;
- VolumeLabel: byte = $08;
- Subdirectory: byte = $10;
- Archive: byte = $20;
-
- NeverUsed: byte = $00;
- Erased: byte = $E5;
-
- FixedDisk: byte = $F8;
- Dual8Sector: byte = $FF;
- Single8Sector: byte = $FE;
- Dual9Sector: byte = $FD;
- Single9Sector: byte = $FC;
-
- Unused: integer = $0000;
-
- var
-
- { Drive characteristics and constants communications block }
-
- DriveLetter: char;
- NumberOfFats,
- Media,
- DefaultDrive,
- DriveNumber: byte;
- FreeClusters,
- TotalDataClusters,
- FirstDataSector,
- FATsize,
- FirstFATsector,
- RootDirSize,
- DirectorySectors,
- FirstDirectorySector,
- SectorSize,
- ReservedMinimum,
- ReservedMaximum,
- BadCluster,
- LastMinimum,
- LastMaximum,
- LastNormal,
- ClusterSize: integer;
-
- { Global variables }
-
- Registers: Regpack;
- OldFATindex,
- NewFATindex,
- Errors,
- LostClusters,
- TotalFiles,
- HiddenFiles,
- InRootDirectory,
- InSubdirectories,
- NonContiguousFiles,
- Subdirectories,
- MovedClusters,
- ClustersToMove,
- Count: integer;
- SAVEaddress,
- DTAddress: ^Buffer;
- PermutationAddress,
- NewFATAddress,
- OldFATAddress: ^IntArray;
- RootDir: DirectoryPointer;
- MovedField,
- InputField,
- LogField,
- WarningField,
- ErrorField,
- DisasterField: LongInteger;
- Anything,
- Instr: char;
- BigFAT,
- NeedReboot,
- AlreadyWritten: boolean;
- DiskLabel: array[0..10] of char;
-
- (* procedure Int25(var Registers: Regpack); external 'Int25.com'; *)
-
- (* procedure Int26(var Registers: Regpack); external 'Int26.com'; *)
-
- {$I REFORMAT.INC}
-
- procedure Beep;
-
- begin
- write(chr(7));
- end;
-
- procedure WriteLog(Text: WorkString);
-
- var
- Count: integer;
-
- begin
- gotoxy(LogField[0], LogField[1]);
- for Count := LogField[0] to 79 do write(' ');
- gotoxy(LogField[0], LogField[1]);
- write(Text);
- end;
-
- procedure WriteWarning(Text: WorkString);
-
- var
- Count: integer;
-
- begin
- gotoxy(WarningField[0], WarningField[1]);
- for Count := WarningField[0] to 79 do write(' ');
- gotoxy(WarningField[0], WarningField[1]);
- write(Text);
- end;
-
- procedure WriteError(Text: WorkString);
-
- var
- Count: integer;
-
- begin
- gotoxy(ErrorField[0], ErrorField[1]);
- for Count := ErrorField[0] to 79 do write(' ');
- gotoxy(ErrorField[0], ErrorField[1]);
- write(Text);
- end;
-
- procedure WriteDisaster(Text: WorkString);
-
- var
- Count: integer;
-
- begin
- gotoxy(DisasterField[0], DisasterField[1]);
- for Count := DisasterField[0] to 79 do write(' ');
- gotoxy(DisasterField[0], DisasterField[1]);
- write(Text);
- end;
-
- procedure GetInput(var Instr: char);
-
- var
- Count: integer;
-
- begin
- gotoxy(InputField[0], InputField[1]);
- for Count := InputField[0] to 79 do write(' ');
- gotoxy(InputField[0], InputField[1]);
- Beep;
- readln(Instr);
- end;
-
- procedure GetInformation;
-
- { Ask DOS for information about the specified or default disk.
- If we have an error return code from DOS we assume that the disk
- specified was invalid. }
-
- var
-
- ValidDrive: boolean;
- InLetter: char;
- Instr: char;
- x: integer;
-
- begin
- { get current disk: MS-DOS function call 19h
- information is returned in AL: 0 = A, 1 = B, etc }
- WriteLog('Reading Disk Information');
- Registers.ah := $19;
- msdos(Registers);
- DefaultDrive := Registers.al;
- if paramcount = 0
- then
- Instr := chr(65 + DefaultDrive)
- else
- Instr := copy(paramstr(1), 1, 1);
- ValidDrive := false;
- BigFAT := false;
- with Registers do repeat
- if ord(Instr) < 64 then Instr := chr($FF);
- DriveLetter := upcase(Instr);
- DriveNumber := ord(DriveLetter) - 64;
- ah := $36;
- dl := DriveNumber;
- msdos(Registers);
- if ax <> $ffff
- then begin
- DriveNumber := DriveNumber - 1;
- FreeClusters := bx;
- TotalDataClusters := dx;
- if TotalDataClusters > 4095 then BigFAT := true;
- Sectorsize := cx;
- ClusterSize := ax;
- FirstFATsector := 1;
- if BigFAT then
- begin
- x := TotalDataClusters - 4096;
- Count := (( x + 2 ) * 4 );
- end
- else
- Count := (( TotalDataClusters + 2 ) * 3 );
- If Count mod ( SectorSize * 2 ) = 0
- then FATsize := Count div ( SectorSize * 2 )
- else FATsize := Count div ( SectorSize * 2 ) + 1;
- If BigFAT then FATsize := FATsize + (4096 div (SectorSize div 2));
- FirstDirectorySector := 2 * FATsize + 1;
- ValidDrive := true;
- if BigFAT then
- begin
- ReservedMinimum := $7FF0;
- ReservedMaximum := $7FF6;
- BadCluster := $7FF7;
- LastMinimum := $7FF8;
- LastMaximum := $7FFF;
- LastNormal := $7FFF;
- end
- else
- begin
- ReservedMinimum := $0FF0;
- ReservedMaximum := $0FF6;
- BadCluster := $0FF7;
- LastMinimum := $0FF8;
- LastMaximum := $0FFF;
- LastNormal := $0FFF;
- end;
- end
- else begin
- WriteWarning('Invalid driveletter, enter new letter!');
- GetInput(Instr);
- WriteWarning(' ');
- end;
- until ValidDrive;
- if DriveNumber = DefaultDrive then
- NeedReboot := true
- else
- NeedReboot := false;
- end;
-
- function CarryFlag: boolean;
-
- begin
- CarryFlag := ( Registers.Flags and $01 ) <> 0 ;
- end;
-
- procedure ResetDisk;
-
- begin
- Registers.ah := $0D;
- msdos(Registers);
- end;
-
- procedure ReadSectors(SectorNumber, NumberOfSectors: integer);
-
- begin
- with Registers do repeat
- al := DriveNumber;
- cx := NumberOfSectors;
- dx := SectorNumber;
- ds := seg(DTAddress^);
- bx := ofs(DTAddress^);
- int2526($25);
- if CarryFlag then begin
- if not AlreadyWritten
- then begin
- WriteWarning('No data lost!');
- WriteError('Disk read error, enter A (abort), R (retry)?');
- end
- else begin
- WriteError('Probably loss of data!');
- WriteDisaster('Disk read error A(bort), R(etry), I(gnore)?');
- end;
- Instr := '?';
- repeat
- Getinput(Instr);
- until ( Instr in ['a', 'A', 'r', 'R'] )
- or (( Instr in ['i', 'I'] ) and AlreadyWritten );
- if Instr in ['a', 'A']
- then begin
- clrscr;
- halt;
- end
- else begin
- WriteError(' ');
- WriteWarning(' ');
- WriteDisaster(' ');
- if Instr in ['i', 'I'] then flags := 0;
- end; end;
- until not CarryFlag;
- end;
-
- procedure WriteSectors(SectorNumber, NumberOfSectors: integer);
-
- begin
- with Registers do repeat
- al := DriveNumber;
- cx := NumberOfSectors;
- dx := SectorNumber;
- ds := seg(DTAddress^);
- bx := ofs(DTAddress^);
- int2526($26);
- if CarryFlag
- then begin
- if not AlreadyWritten
- then begin
- WriteWarning('No data lost!');
- WriteError('Disk write error, enter A (abort), R (retry)?');
- end
- else begin
- WriteError('Probably data lost!');
- WriteDisaster('Disk write error A(bort), R(etry), I(gnore)?');
- end;
- repeat
- Getinput(Instr);
- until ( Instr in ['a', 'A', 'r', 'R'] )
- or (( Instr in ['i', 'I'] ) and AlreadyWritten );
- if Instr in ['a', 'A']
- then begin
- clrscr;
- halt;
- end
- else begin
- WriteError(' ');
- WriteWarning(' ');
- WriteDisaster(' ');
- if Instr in ['i', 'I'] then flags := 0;
- end; end;
- until not CarryFlag;
- AlreadyWritten := true;
- end;
-
- procedure ReadCluster(ClusterNumber: integer);
-
- var
- SectorNumber: integer;
-
- begin
- { To get around Turbo's maxint, (in case of fixed disks of 20 MB the largest
- sectornumber is greater than 32767) we split the following formula:
-
- SectorNumber := ClusterSize * ( ClusterNumber - 2 ) + FirstDataSector;
-
- Multiplication does not return a correct value when Sectornumber becomes
- greater than maxint. Addition returns a word value (16 bits) that is the
- correct sectornumber if interpreted as a non-signed integer.
- Since ClusterSize is ALWAYS (PC-DOS TECH REF: chap Device Drivers,
- boot record layout) a power of 2, we may divide it by 2. }
-
- if ClusterSize < 2
- then SectorNumber := ClusterNumber - 2 + FirstDataSector
- else SectorNumber := ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
- ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
- FirstDataSector;
- ReadSectors(SectorNumber, ClusterSize);
- end;
-
- procedure WriteCluster(ClusterNumber: integer);
-
- var
- SectorNumber: integer;
-
- begin
- { To get around Turbo's maxint, (in case of fixed disks of 20 MB the largest
- sectornumber is greater than 32767) we split the following formula:
-
- SectorNumber := ClusterSize * ( ClusterNumber - 2 ) + FirstDataSector;
-
- Multiplication does not return a correct value when Sectornumber becomes
- greater than maxint. Addition returns a word value (16 bits) that is the
- correct sectornumber if interpreted as a non-signed integer.
- Since ClusterSize is ALWAYS (PC-DOS TECH REF: chap Device Drivers,
- boot record layout) a power of 2, we may divide it by 2. }
-
- if ClusterSize < 2
- then SectorNumber := ClusterNumber - 2 + FirstDataSector
- else SectorNumber := ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
- ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
- FirstDataSector;
- WriteSectors(SectorNumber, ClusterSize);
- end;
-
- procedure ReadBootSector(var DTArea: Buffer);
-
- { Read the bootsector from disk. Use the information we find in it
- to set a number of variables in the communication block. If the
- information in the bootsector is inconsistent with the story DOS
- told us (GetInformation) we use the FAT identification byte for
- the setting of the variables. This will probably only occur in
- case we have a disk that was formatted under a pre DOS 2.0 version.}
-
- var
- FATidentification: byte;
- Instr: char;
- BootInfo: Boot absolute DTArea;
-
- begin
- WriteLog('Reading Bootsector.');
- ReadSectors(0, 1);
- if ( TotalDataClusters >= 16284 )
- or ( TotalDataClusters < 0 )
- then begin
- WriteWarning('Disk contains too many clusters for program.');
- WriteError('Program limit is 16283 clusters.');
- WriteDisaster('Press enter to return to DOS.');
- GetInput(Instr);
- clrscr;
- halt;
- end;
- if ( BootInfo.SectorSize <> SectorSize )
- or ( BootInfo.ClusterSize <> Clustersize )
- or ( BootInfo.NumberOfFats = 0 )
- or ( BootInfo.RootDirSize = 0 )
- or ( BootInfo.TotalSectors < TotalDataClusters * ClusterSize )
- or not ( BootInfo.MediaDescriptor in [$F0..$FF] )
- or ( BootInfo.FATsize <> FATsize )
- then begin
- WriteWarning('Pre DOS 2.0 formatted disk, or incomplete bootsector.');
- ReadSectors(FirstFATsector, 1);
- FATidentification := DTArea[0];
- NumberOfFATs := 2;
- if ( FATidentification = Single8Sector )
- or ( FATidentification = Single9Sector )
- then RootDirSize := 64
- { Not Single Sided }
- else if ( FATidentification = Dual8Sector )
- or ( FATidentification = Dual9Sector )
- then RootDirSize := 112
- else if FATidentification = FixedDisk
- { Fixed Disk }
- then begin
- WriteError('Fixed Disk: cannot compute size.');
- WriteDisaster('Press enter to return to DOS.');
- GetInput(Instr);
- clrscr;
- halt;
- end
- else begin
- WriteError('Unknown Disk Type (FAT id byte).');
- WriteDisaster('Press enter to return to DOS.');
- GetInput(Instr);
- clrscr;
- halt;
- end;
- FirstDataSector := NumberOfFats * Fatsize +
- RootDirSize * 32 div SectorSize + 1;
- Media := FATidentification;
-
- end
- else begin
- NumberOfFats := BootInfo.NumberOfFats;
- if NumberOfFats <> 2
- then FirstDirectorySector := FATsize * NumberOfFats + 1;
- RootDirSize := BootInfo.RootDirSize;
- FirstDataSector := NumberOfFats * Fatsize +
- RootDirSize * 32 div SectorSize + 1;
- Media := BootInfo.MediaDescriptor;
- end;
- end;
-
- procedure ReadFat(var UnscrambledFAT: IntArray; var ScrambledFAT: Buffer);
-
- { Read and unscramble the FAT. Only the first FAT is processed.}
-
- var
- i, Temp: integer;
-
- begin
- WriteLog('Reading and unscrambling FAT.');
- ReadSectors(FirstFATsector, FATsize);
- for i := 0 to TotalDataClusters + 1 do begin
- if BigFAT then
- begin
- move( ScrambledFAT[i * 2], Temp, 2);
- temp := temp and $7FFF;
- unscrambledFAT[i] := Temp;
- end
- else
- begin
- move( ScrambledFAT[3 * i div 2], Temp, 2);
- if odd(i) then Temp := Temp shr 4 else Temp := Temp and $0FFF;
- UnscrambledFAT[i] := Temp;
- end;
- end;
- end;
-
- procedure WriteFat(var UnscrambledFAT: IntArray; var ScrambledFAT: Buffer);
-
- { Write the FAT back to the disk. The FAT has to be scrambled before
- writing. FAT entries on disk are 12 bits long. Because there are mostly
- 2 versions of the fat on disk, we write both fats simultaneously.}
-
- var
- i,
- Temp1,
- Temp2: integer;
-
- begin
- WriteLog('Writing FAT.');
- for i := 0 to TotalDataClusters + 1 do begin
- if BigFAT then
- begin
- Temp1 := UnscrambledFAT[i];
- if (Temp1 and $4000) <> 0 then Temp1 := Temp1 or $8000;
- move( Temp1, ScrambledFAT[i * 2], 2);
- end
- else
- begin
- Temp1 := UnscrambledFAT[i];
- move( ScrambledFAT[3 * i div 2], Temp2, 2);
- if odd(i) then Temp1 := (Temp2 and $000F) or (Temp1 shl 4)
- else Temp1 := (Temp2 and $F000) or Temp1;
- move( Temp1, ScrambledFAT[3 * i div 2], 2);
- end;
- end;
- WriteSectors(FirstFATsector, FATsize);
- WriteSectors(FirstFATsector + FATsize, FATsize);
- end;
-
- procedure ReadSubdirectory(var DTArea: Buffer;
- var FATarea: INTArray;
- var SubRoot: DirectoryPointer;
- StartingCluster: integer);
-
- { Link subdirectory entries in a list. Build a tree (by calling this
- routine recursively) if a subdirectory is found.}
-
- var
- ClusterNumber,
- DirIndex: integer;
- Present: DirectoryPointer;
- EndSearch: boolean;
-
- begin
- Subdirectories := Subdirectories + 1;
- ClusterNumber := StartingCluster;
- SubRoot := nil;
- EndSearch := false;
- repeat
- ReadCluster(ClusterNumber);
- DirIndex := 0;
- repeat
- if not ( DTArea[DirIndex] in [NeverUsed, Erased] )
- then begin
- if SubRoot = nil
- then begin
- new(SubRoot);
- Present := SubRoot;
- end
- else begin
- new(Present^.Next);
- Present := Present^.Next;
- end;
- move(DTArea[DirIndex], Present^, 32);
- if ( Present^.Attribute = Subdirectory ) and
- ( Present^.EntryName[0] <> '.' )
- then begin
- ReadSubdirectory(DTArea, FATarea, Present^.SubDirectory,
- Present^.StartingCluster);
- Readcluster(ClusterNumber);
- end
- else begin
- Present^.SubDirectory := nil;
- if Present^.Entryname[0] <> '.'
- then begin
- TotalFiles := TotalFiles + 1;
- InSubdirectories := InSubdirectories + 1;
- if ( Present^.Attribute and HiddenFile ) <> 0
- then HiddenFiles := HiddenFiles + 1;
- end; end; end
- else if DTArea[DirIndex] = NeverUsed
- then EndSearch := true;
- DirIndex := DirIndex + 32;
- until ( DirIndex >= SectorSize * ClusterSize)
- or ( EndSearch );
- ClusterNumber := FATarea[ClusterNumber];
- until ( ClusterNumber >= ReservedMinimum ) or EndSearch;
- if Present <> nil then Present^.Next := nil;
- end;
-
- procedure ReadDirectories(var DTArea: Buffer);
-
- { Read the Rootdirectory and whenever an entry for a subdirectory is
- found call ReadSubdirectory. Link all directory entries dynamically
- in a linked list. This list is actually a tree, because the lists
- for subdirectories are linked to this list.}
-
- var
- EndSearch: boolean;
- SectorNumber,
- DirIndex: integer;
- Present: DirectoryPointer;
-
- begin
- WriteLog('Reading Directory and Subdirectories.');
- SectorNumber := FirstDirectorySector;
- RootDir := nil;
- EndSearch := false;
- repeat
- DirIndex := 0;
- ReadSectors(SectorNumber, 1);
- repeat
- if not ( DTArea[DirIndex] in [NeverUsed, Erased] )
- then begin
- if RootDir = nil
- then begin
- new(RootDir);
- Present := RootDir;
- end
- else begin
- new(Present^.Next);
- Present := Present^.Next;
- end;
- move(DTArea[DirIndex], Present^, 32);
- if ( Present^.Attribute = Subdirectory ) and
- ( Present^.EntryName[0] <> '.' )
- then begin
- ReadSubdirectory(DTArea, OldFATaddress^,
- Present^.SubDirectory,
- Present^.StartingCluster);
- ReadSectors(SectorNumber, 1);
- end
- else begin
- Present^.SubDirectory := nil;
- if ( Present^.Attribute <> VolumeLabel ) and
- ( Present^.Entryname[0] <> '.' )
- then begin
- TotalFiles := TotalFiles + 1;
- InRootDirectory := InRootDirectory + 1;
- if ( Present^.Attribute and HiddenFile ) <> 0
- then HiddenFiles := HiddenFiles + 1;
- end; end; end
- else if DTArea[DirIndex] = NeverUsed
- then EndSearch := true;
- DirIndex := DirIndex + 32;
- until ( DirIndex >= SectorSize ) or EndSearch;
- SectorNumber := SectorNumber + 1;
- until ( SectorNumber = FirstDataSector ) or EndSearch;
- if Present <> nil then Present^.Next := nil;
- end;
-
- procedure RemakeFAT(var OldFATarea, NewFATarea, Permutation: IntArray;
- Root: DirectoryPointer; Parent, ThisDir: integer);
-
- { This procedure is called recursively.
- From the OldFAT and the directory entries we construct a NewFAT and
- a Permutation. The Permutation is used by DoIt for moving the
- clusters. This routine is called one extra time for the chain of
- the empty clusters by LinkFreeDataClusters.
- Recursion is used whenever we find an entry for a subdirectory, in
- the following way: first call this routine for the remainder of the
- current directory, second for the subdirectory.
- The function NewFATindex is used to prevent accidental use of clusters
- that were marked as bad or reserved clusters.}
-
- function NextFATindex: integer;
-
- var
- Temp: integer;
-
- begin
- Temp := NewFATindex + 1;
- while ( OldFATarea[Temp] >= ReservedMinimum ) and
- ( OldFATarea[Temp] <= BadCluster ) and
- ( Temp <= TotalDataClusters + 1 )
- do begin
- NewFATarea[Temp] := OldFATarea[Temp];
- Temp := Temp + 1;
- end;
- NextFATindex := Temp;
- end;
-
- var
- Present: DirectoryPointer;
- Split: boolean;
- Temp: integer;
-
- begin
- if NewFATindex = 1 then NewFATindex := NextFatindex;
- Present := Root;
- Split := false;
- while ( Present <> nil ) and not Split do begin
- if ( Present^.Attribute <> VolumeLabel ) and
- ( Present^.StartingCluster <> 0 ) and
- ( Present^.Entryname[0] <> '.')
- then begin
- if Present^.SubDirectory <> nil
- then begin
- Split := true;
- RemakeFAT(OldFATarea, NewFATarea, Permutation,
- Present^.Next, Parent, ThisDir);
- end;
- OldFATindex := Present^.StartingCluster;
- Present^.NewStartingCluster := NewFatindex;
- Permutation[NewFATindex] := OldFATindex;
- while OldFATarea[OldFATindex] < LastMinimum do begin
- Temp := NextFatindex;
- NewFATarea[NewFATindex] := Temp;
- NewFatindex := Temp;
- OldFATindex := OldFATarea[OldFATindex];
- Permutation[NewFATindex] := OldFATindex;
- end;
- NewFatArea[NewFATindex] := LastNormal;
- NewFATindex := NextFatindex;
- if Split then
- RemakeFAT(OldFATarea, NewFATarea, Permutation,
- Present^.SubDirectory, ThisDir,
- Present^.NewStartingCluster);
- end
- else begin
- if ( Present^.EntryName[0] = '.' ) and
- ( Present^.EntryName[1] = '.' )
- then Present^.NewStartingCluster := Parent
- else if Present^.EntryName[0] = '.'
- then Present^.NewStartingCluster := ThisDir
- else begin
- Present^.NewStartingCluster := 0;
- if Present^.Attribute = VolumeLabel
- then for Count := 0 to 10 do
- DiskLabel[Count] := Present^.EntryName[Count];
- end; end;
- Present := Present^.Next;
- end;
- end;
-
- procedure LinkFreeClusters(var OldFATarea, NewFATarea: IntArray);
-
- { Link Free clusters in a chain, pointed to by Empty^.
- Use RemakeFAT to fill Permutation, but clean NewFAT after
- this. This procedure will ensure that permutation is a
- proper permutation, without double entries which might
- cause DoIt to loop indefinitely or destroy our disk. }
-
- var
- Count,
- Next,
- Previous: integer;
- Empty: DirectoryPointer;
-
- begin
- new(Empty);
- Empty^.Next := nil;
- Empty^.SubDirectory := nil;
- Empty^.Entryname[0] := 'X';
- Empty^.Attribute := HiddenFile;
- Empty^.StartingCluster := 0;
- Count := 2;
- while ( Count <= TotalDataClusters + 1 ) and
- ( OldFATarea[Count] <> 0 )
- do Count := Count + 1;
- if Count <= TotalDataClusters + 1
- then begin
- Empty^.StartingCluster := Count;
- Previous := Count;
- while Count < TotalDataClusters + 1
- do begin
- Count := Count + 1;
- if OldFATarea[Count] = 0
- then begin
- OldFATarea[Previous] := Count;
- Previous := Count;
- end; end;
- OldFATarea[Previous] := LastNormal;
- end;
- if Empty^.StartingCluster <> 0
- then begin
- RemakeFAT(OldFATarea, NewFATarea,
- PermutationAddress^, Empty, 0, 0);
- Next := Empty^.NewStartingCluster;
- while Next <> LastNormal
- do begin
- Previous := Next;
- Next := NewFATarea[Previous];
- NewFatarea[Previous] := 0;
- end; end;
- end;
-
- procedure WriteSubdirectory(var DTArea: Buffer; var OldFATarea: IntArray;
- Root: DirectoryPointer; Start: integer);
-
- { Write subdirectories back to disk. Erased entries are removed
- from the subdirectories. The subdirectories are written to their
- old locations, because DoIt will take care of moving the clusters
- to their new places. No effort is done to truncate a subdirectory
- which would be longer than needed after removal of erased entries.
- We will however set all remaining entries to 'NeverUsed'.
- This routine is used recursively.}
-
- var
- Start1,
- ClusterNumber,
- DirIndex: integer;
- Present: DirectoryPointer;
-
- begin
- Present := Root;
- ClusterNumber := Start;
- while Present <> nil
- do begin
- DirIndex := 0;
- fillchar(DTArea, ClusterSize * SectorSize, $00);
- repeat
- Start1 := Present^.StartingCluster;
- Present^.StartingCluster := Present^.NewStartingCluster;
- move(Present^, DTArea[DirIndex], 32);
- if ( Present^.Attribute = SubDirectory ) and
- ( Present^.EntryName[0] <> '.' )
- then begin
- WriteCluster(ClusterNumber);
- WriteSubdirectory(DTArea, OldFATarea,
- Present^.SubDirectory, Start1);
- ReadCluster(ClusterNumber);
- end;
- Present := Present^.Next;
- DirIndex := DirIndex + 32;
- until ( DirIndex >= ClusterSize * SectorSize ) or ( Present = nil );
- WriteCluster(ClusterNumber);
- ClusterNumber := OldFATarea[ClusterNumber];
- end;
- if ClusterNumber < LastMinimum
- then begin
- fillchar(DTArea, SectorSize * ClusterSize, $00);
- while ClusterNumber < LastMinimum
- do begin
- WriteCluster(ClusterNumber);
- ClusterNumber := OldFATarea[ClusterNumber];
- end; end;
- end;
-
- procedure WriteDirectories(var DTArea: Buffer);
-
- { Write rootdirectory back to disk. Erased entries are removed
- from the directory. When we find a subdirectory entry, we first
- process this subdirectory by calling WriteSubdirectories,
- before we proceed with the root. All entries that are no in use
- are set to 'NeverUsed'.}
-
- var
- Start,
- SectorNumber,
- DirIndex: integer;
- Present: DirectoryPointer;
-
- begin
- WriteLog('Writing new Directory and Subdirectories.');
- SectorNumber := FirstDirectorySector;
- Present := RootDir;
- while Present <> nil
- do begin
- DirIndex := 0;
- fillchar(DTArea, SectorSize, $00);
- repeat
- Start := Present^.StartingCluster;
- Present^.StartingCluster := Present^.NewStartingCluster;
- move(Present^, DTArea[DirIndex], 32);
- if ( Present^.Attribute = SubDirectory ) and
- ( Present^.EntryName[0] <> '.' )
- then begin
- WriteSectors(SectorNumber, 1);
- WriteSubdirectory(DTArea, OldFATaddress^,
- Present^.SubDirectory, Start);
- ReadSectors(SectorNumber, 1);
- end;
- Present := Present^.Next;
- DirIndex := DirIndex + 32;
- until ( DirIndex >= SectorSize ) or ( Present = nil );
- WriteSectors(SectorNumber, 1);
- SectorNumber := SectorNumber + 1;
- end;
- if SectorNumber < FirstDataSector
- then begin
- fillchar(DTArea, SectorSize, $00);
- while SectorNumber < FirstDataSector
- do begin
- WriteSectors(SectorNumber, 1);
- SectorNumber := SectorNumber + 1;
- end; end;
- end;
-
- procedure DoIt(var Permutation: IntArray; var DTArea, SaveArea: Buffer);
-
- { DoIt. This routine performs the actual reformating of the disk.
- The array Permutation contains in every location [i] (starting
- from 2) which cluster has to be moved to cluster location i.
- Because we have a real permutation, this permutation can be
- parsed into a number of cyclical permutations. We start at the
- first cyclic permutation that is not identity. We save the first
- cluster of this cyclical permutation, proceed through the cyclical
- permutation, moving one cluster at a time, until we finish the
- cycle. We than write the saved cluster to disk.}
-
- var
- Prior,
- Next,
- LastStart: integer;
-
- begin
-
- WriteLog('Reformatting......');
- LastStart := 2;
- while LastStart <= TotalDataClusters + 1
- do begin
- if LastStart = Permutation[LastStart]
- then LastStart := LastStart + 1
- else begin
- ReadCluster(LastStart);
- move(DTArea, SaveArea, SectorSize * ClusterSize);
- Prior := LastStart;
- Next := Permutation[LastStart];
- repeat
- ReadCluster(Next);
- WriteCluster(Prior);
- MovedClusters := MovedClusters + 1;
- gotoxy(MovedField[0], MovedField[1]);
- write(MovedClusters:10);
- Permutation[Prior] := Prior;
- Prior := Next;
- Next := Permutation[Next];
- until Next = LastStart;
- move(SaveArea, DTArea, SectorSize * ClusterSize);
- WriteCluster(Prior);
- MovedClusters := MovedClusters + 1;
- gotoxy(MovedField[0], MovedField[1]);
- write(MovedClusters:10);
- Permutation[Prior] := Prior;
- end; end;
- WriteLog(' ');
- end;
-
- procedure InitScreen;
-
- var
- Row,
- Column: integer;
- begin
- normvideo;
- clrscr;
- Row := 2;
- write(char(201)); for Column := 2 to 79 do write(char(205));
- write(char(187));
- write(char(186)); gotoxy(80, Row);
- write(char(186));
- gotoxy(15, Row); write('REFORMAT: an original JOS disk tool. Ver: 1.21(mod)');
- Row := Row + 1; gotoxy(1, Row);
- write(char(199)); for Column := 2 to 79 do write(char(196));
- write(char(182));
- for Row := 4 to 15 do
- begin
- write(char(186)); gotoxy(80, Row);
- write(char(186));
- end;
- write(char(199)); for Column := 2 to 79 do write(char(196));
- write(char(182));
- write(char(186)); gotoxy(80, 17);
- write(char(186));
- write(char(199)); for Column := 2 to 79 do write(char(196));
- write(char(182));
- for Row := 19 to 23 do
- begin
- write(char(186)); gotoxy(80, Row);
- write(char(186));
- end;
- write(char(200)); for Column := 2 to 79 do write(char(205));
- write(char(188));
- gotoxy(05, 19); write('User Input Field :');
- gotoxy(05, 20); write('Activity Logging :');
- gotoxy(05, 21); write('Warning Messages:');
- gotoxy(05, 22); write('Error Messages:');
- gotoxy(05, 23); write('Disaster Messages:');
- InputField[0] := 24;
- InputField[1] := 19;
- LogField[0] := 24;
- LogField[1] := 20;
- WarningField[0] := 24;
- WarningField[1] := 21;
- ErrorField[0] := 24;
- ErrorField[1] := 22;
- DisasterField[0] := 24;
- DisasterField[1] := 23;
- end;
-
- procedure CheckSubdirectory(var FAT: IntArray;
- Root: DirectoryPointer; Parent, ThisDir: integer);
-
- { This procedure is called recursively.
- The SubDirectories are checked here. No attempt is made
- to correct any errors found. If any errors are found, a message
- is issued and the program stops. The users must first run CHKDSK from
- DOS before we accept the disk. }
-
- var
- Present: DirectoryPointer;
- Prior,
- Next: integer;
-
- begin
- Present := Root;
- while ( Present <> nil ) and ( Errors = 0 ) begin
- if ( Present^.Attribute <> VolumeLabel ) and
- ( Present^.StartingCluster <> 0 ) and
- ( Present^.Entryname[0] <> '.')
- then begin
- Next := Present^.StartingCluster;
- Count := 0;
- repeat;
- if ( Next > TotalDataClusters + 1 )
- or ( Next < 1 )
- then begin
- Errors := Errors + 1;
- end
- else begin
- Prior := Next;
- Next := FAT[Prior];
- FAT[Prior] := 0;
- if Next <> Prior + 1 then Count := Count + 1;
- end;
- until ( Next >= LastMinimum ) or ( Errors <> 0 );
- if Count > 1 then NonContiguousFiles := NonContiguousFiles + 1;
- if Present^.SubDirectory <> nil
- then CheckSubdirectory(FAT, Present^.SubDirectory,
- ThisDir, Present^.StartingCluster);
- end
- else begin
- if ( Present^.EntryName[0] = '.' ) and
- ( Present^.EntryName[1] = '.' )
- then if Present^.StartingCluster <> Parent
- then Errors := Errors + 1
- else
- else if Present^.EntryName[0] = '.'
- then if Present^.StartingCluster <> ThisDir
- then Errors := Errors + 1
- else
- else if Present^.StartingCluster <> 0
- then Errors := Errors + 1;
- end;
- Present := Present^.Next;
- end;
- end;
-
- procedure CheckDisk(var FAT: IntArray; Root: DirectoryPointer);
-
- { The FAT and the Directories are checked here. No attempt is made
- to correct any errors found. If any errors are found, a message
- is issued and the program stops. The users must first run CHKDSK from
- DOS before we accept the disk. }
-
- begin
- WriteLog('Checking FAT....');
- CheckSubdirectory(FAT, Root, 0, 0);
- for Count := 2 to TotalDataClusters + 1 do
- if ( FAT[Count] <> 0 ) and
- ( ( FAT[Count] < ReservedMinimum ) or
- ( FAT[Count] > BadCluster ) )
- then LostClusters := LostClusters + 1;
- if Errors <> 0
- then begin
- WriteError('Crosslinked clusters found. Run CHKDSK first.');
- WriteWarning('Press Enter to return to DOS.');
- GetInput(Instr);
- clrscr;
- halt;
- end
- else if LostClusters <> 0
- then begin
- WriteError('Lost clusters found. Run CHKDSK first.');
- WriteWarning('Press Enter to return to DOS.');
- GetInput(Instr);
- clrscr;
- halt;
- end;
- end;
-
- procedure CountClustersToMove(var Permutation: IntArray);
-
- begin
- for Count := 2 to TotalDataClusters + 1
- do if Permutation[Count] <> Count then ClustersToMove := ClustersToMove + 1;
- end;
-
- procedure InitCounters;
-
- begin
- OldFATindex := 0;
- NewFATindex := 1;
- Errors := 0;
- LostClusters := 0;
- TotalFiles := 0;
- HiddenFiles := 0;
- InRootDirectory := 0;
- InSubdirectories := 0;
- NonContiguousFiles := 0;
- Subdirectories := 0;
- MovedClusters := 0;
- ClustersToMove := 0;
- Count := 0;
- AlreadyWritten := false;
- DiskLabel := ' ';
- end;
-
- procedure WriteStatistics;
-
- var
- Row: integer;
-
- begin
- if NonContiguousFiles = 0 then ClustersToMove := 0;
- Row := 5;
- if DiskLabel <> ' '
- then begin
- gotoxy(18, Row); write('Volume Label is . . . . . : ', DiskLabel);
- Row := Row + 1;
- end;
- gotoxy(18, Row); write( 'Total # of files. . . . . :', TotalFiles:10);
- if HiddenFiles <> 0
- then write(' (hidden:', HiddenFiles:3,')');
- Row := Row + 1;
- if Subdirectories = 0
- then begin
- gotoxy(18, Row); write('All files in Rootdirectory.');
- end
- else begin
- gotoxy(18, Row); write(' in Root directory . . . :',
- InRootDirectory:10);
- Row := Row + 1;
- gotoxy(18, Row); write(' in ', Subdirectories:3, ' Subdirectories . :',
- InSubDirectories:10);
- end;
- Row := Row + 1;
- gotoxy(18, Row); write('# of noncontiguous files. :',
- NonContiguousFiles:10);
- Row := Row + 1;
- gotoxy(18, Row); write('# of clusters to be moved :',
- ClustersToMove:10);
- Row := Row + 1;
- gotoxy(18, Row); write('# of clusters moved . . . :',
- MovedClusters:10);
- MovedField[0] := 45;
- MovedField[1] := Row;
- Row := Row + 2;
- gotoxy(05, Row); write('Clustersize . . :', ClusterSize:06,
- ' sectors.');
- gotoxy(45, Row); write('Sectorsize. . . :', SectorSize:06,
- ' bytes.');
- Row := Row + 1;
- gotoxy(05, Row); write('Total data space:', TotalDataClusters:6,
- ' clusters.');
- gotoxy(45, Row); write('DOS space . . . :', FirstDataSector:6,
- ' sectors.');
- Row := Row + 1;
- gotoxy(05, Row); write('Free data space :', FreeClusters:6,
- ' clusters.');
- gotoxy(45, Row); write('Disk type . . . :');
- case Media of
- $F8: { FixedDisk } write(' Fixed Disk');
- $FE: { Single8Sector} write(' 1 sided / 8 sect');
- $FF: { Dual8Sector } write(' 2 sided / 8 sect');
- $FC: { Single9sector} write(' 1 sided / 9 sect');
- $FD: { Dual9sector } write(' 2 sided / 9 sect');
- end;
- end;
-
- procedure WriteDoc;
-
- begin
- clrscr;
- writeln;
- writeln(' REFORMAT: an original JOS disk tool.');
- writeln;
- writeln(' Public Domain Software.');
- writeln;
- writeln('Makes all files on a floppy or fixed disk contiguous again,');
- writeln('improving disk performance dramatically. Either fixed disks');
- writeln('or diskettes. Requires DOS 2.xx.');
- writeln('Register at the following address to be on my mailing list for');
- writeln('updates:');
- writeln;
- writeln(' Jos Wennmacker');
- writeln(' Universitair Rekencentrum');
- writeln(' Geert Grooteplein Zuid 41');
- writeln(' NL-6525 GA Nijmegen');
- writeln(' The Netherlands');
- writeln;
- writeln;
- writeln;
- writeln('Also comments, bugs etc are expected at one of these addresses.');
- writeln;
- writeln(' Press enter to see next page');
- readln;
- clrscr;
- writeln;
- writeln(' REFORMAT: an original JOS disk tool.');
- writeln;
- writeln(' Public Domain Software.');
- writeln;
- writeln;
- writeln('Use: Reformat [d:]');
- writeln;
- writeln('where d: is an optional driveletter. Ommiting d: will select the');
- writeln('default drive. This program works for both fixed disks and');
- writeln('floppies.');
- writeln;
- writeln('* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING **');
- writeln;
- writeln('NEVER use this program on a disk that contains * PROTECTED *');
- writeln('software. You might find these programs turned into an illegal');
- writeln('copy or even end up with a scrambled disk!!!!!!');
- writeln('Always *UNINSTALL* this kind of software before using REFORMAT!!');
- writeln('The program will prompt you to confirm this in case of a fixed');
- writeln('disk.');
- writeln;
- end;
-
- begin
- if paramcount <> 0
- then if copy(paramstr(1), 1, 1) = '?'
- then begin
- WriteDoc;
- halt;
- end
- else begin
- if ( paramcount > 1 )
- or ( length(paramstr(1)) > 2 )
- or ( (length(paramstr(1)) = 2 ) and
- (copy(paramstr(1), 2, 1) <> ':') )
- then begin
- writeln;
- writeln('Invalid parameter: REFORMAT [d:] or ?.');
- halt;
- end; end;
- InitCounters;
- InitScreen;
- GetInformation;
- if ClusterSize < FATsize
- then getmem(DTAddress, SectorSize * FATsize)
- else getmem(DTAddress, SectorSize * ClusterSize);
- getmem(SAVEaddress, SectorSize * ClusterSize);
- getmem(PermutationAddress, TotalDataClusters * 2 + 4);
- getmem(OldFATaddress, TotalDataClusters * 2 + 4);
- getmem(NewFATaddress, TotaldataClusters * 2 + 4);
- ReadBootSector(DTAddress^);
- ReadFat(OldFATaddress^, DTAddress^);
- ReadDirectories(DTAddress^);
- move(OldFATaddress^, NewFATaddress^, TotalDataClusters * 2 + 4);
- CheckDisk(NewFATaddress^, RootDir);
- fillchar(NewFATaddress^, TotalDataClusters * 2 + 4, 0);
- for Count := 0 to TotalDataClusters + 1 do
- PermutationAddress^[Count] := Count;
- move(OldFATaddress^, NewFATaddress^, 4);
- RemakeFAT(OldFATaddress^, NewFATaddress^,
- PermutationAddress^, RootDir, 0, 0);
- LinkFreeClusters(OldFATaddress^, NewFATaddress^);
- CountClustersToMove(PermutationAddress^);
- WriteStatistics;
- if NonContiguousFiles <> 0
- then begin
- if Media = FixedDisk
- then begin
- gotoxy(05, 17);
- write ('Fixed disk: did you uninstall all protected software? ',
- 'Continue (Y/N)?');
- Instr := 'Q';
- while not ( Instr in ['Y', 'y', 'N', 'n'] )
- do GetInput(Instr);
- if Instr in ['N', 'n']
- then begin
- WriteWarning('Press Enter to return to DOS.');
- GetInput(Instr);
- clrscr;
- halt;
- end; end;
- ResetDisk;
- WriteFAT(NewFATaddress^, DTAddress^);
- WriteDirectories(DTAddress^);
- DoIt(PermutationAddress^, DTAddress^, SAVEaddress^);
- ResetDisk;
- if NeedReboot then
- begin
- repeat
- begin
- WriteLog('Done ! Please reboot system to continue');
- GetInput(Anything);
- end;
- until 1 = 2;
- end
- else
- WriteLog('Done ! Press Enter-Key to return to DOS.');
- end
- else begin
- WriteWarning('All files are contiguous. Nothing to be done!');
- WriteLog('Press Enter-Key to return to DOS.');
- end;
- GetInput(Anything);
- clrscr;
- end.